package org.sensornet2010.sensmote;

import java.io.IOException;
import java.util.Date;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.ImageItem;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.StringItem;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
import javax.microedition.rms.RecordStoreException;

import org.sensornet2010.alpha.Configuration;
import org.sensornet2010.alpha.Preferences;
import org.sensornet2010.alpha.RemoteServer;
import org.sensornet2010.alpha.SensorData;
import org.sensornet2010.alpha.SoundRecorder;
import org.sensornet2010.core.SensornetApp;
import org.sensornet2010.core.iSensornetAppListener;


/*
 * NAMING POLICIES:
 * 
 *  showXXXDisplay() synchronous  
 *  startXXXAction() asynchronous action
 *  updateXXXDisplay() synchronous
 *  
 *  xxxCommand  
 */

public class AMote extends MIDlet  implements CommandListener, iSensornetAppListener {

	// Configuration
	private static final String CONFIG_FILE						= "AMOTE_PREFERENCES";
	private static final String CONFIG_KEY_RECORDING_DURATION	= "DURATION";
	private static final String CONFIG_KEY_RECORDING_INTERVAL	= "INTERVAL";
	private static final String CONFIG_KEY_SERVER_URL			= "SERVER";
	private static final String CONFIG_SENSOR_ID				= "SENSOR_ID";
	private static final String CONFIG_SENSUINO_VERSION			= "SENSUINO_VERSION";
	
	// animation
	private static String RESOURCE_IMAGE_ANIM_A		= "/icons/molecule_48x23.png";
	private static String RESOURCE_IMAGE_ANIM_B		= "/icons/molecule2_48x23.png";
	private Image animationImageA;
	private Image animationImageB;
	private ImageItem	animationItem;
	
	// Commands
	private Command exitCmd;
	private Command configCmd;
	private Command updateConfigCmd;
	private Command measureCmd;
	private Command welcomeCmd;
		
	// GUI components
	private Display		display; 				// The display for this MIDlet
	private StringItem	measurementStatusString;

	// 
	private SensornetApp 	sensornet;
	private Preferences		preferences;

	public AMote(){
		this.display = Display.getDisplay(this);
		
		this.exitCmd = new Command("Salir", Command.EXIT, 1);
		this.configCmd = new Command("Configuracion", Command.SCREEN, 1);
		this.measureCmd = new Command("Medir", Command.SCREEN, 1);
		this.updateConfigCmd = new Command("Guardar", Command.SCREEN, 1);
		this.welcomeCmd = new Command("Inicio", Command.SCREEN, 1);
	}

	protected void startApp() throws MIDletStateChangeException {
		try {
			loadConfiguration();
			showWelcomeDisplay();
		} catch (Exception e) {
			this.showErrorDisplay(e);
		}
	}
	
	protected void destroyApp(boolean unconditional) {
	}

	protected void pauseApp() {
	}
	
	public void commandAction(Command c, Displayable d) {
		try {

			if (c == exitCmd) {
				destroyApp(false);
				notifyDestroyed();
			} else if (c == configCmd) {
				showConfigurationDisplay();
			} else if (c == measureCmd) {
				showMeasurementDisplay();
				startMeasurement();
			} else if (c == updateConfigCmd) {
				doUpdateConfiguration(d);
				showWelcomeDisplay();
			} else if (c == welcomeCmd) {
				showWelcomeDisplay();
			}
		} catch (Exception e) {
			showErrorDisplay(e);
		}
	}

	
	private Form buildConfigurationDisplay(){
		Form f = new Form("Configuration");
		f.append( new TextField("Sensor ID:", Configuration.SENSOR_ID, 12, TextField.ANY) );
		f.append( new TextField("Server:", Configuration.SERVER_URL, 250 , TextField.URL) );
		f.append( new TextField("Acq duration (ms):", Long.toString(Configuration.RECORDING_DURATION) , 10 , TextField.NUMERIC) );
		f.append( new TextField("Sampling (s):", Integer.toString(Configuration.RECORDING_INTERVAL), 10 , TextField.NUMERIC) );
		f.append( new TextField("Sensuino Version:", Configuration.SENSUINO_VERSION, 20 , TextField.ANY) );
		f.addCommand(updateConfigCmd);
		f.addCommand(welcomeCmd);
		f.setCommandListener(this);
		return f;
	}
	private Form buildMeasurementDisplay(){
		Form f = new Form("Measuring");
		StringItem item =  new StringItem( Configuration.SENSOR_ID, "");
		item.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_LARGE));
		f.append(item);
		
		try {
			this.animationImageA = Image.createImage(RESOURCE_IMAGE_ANIM_A);
			this.animationImageB = Image.createImage(RESOURCE_IMAGE_ANIM_B);
		} catch (IOException e) {
			showErrorDisplay(e);
		}
		this.animationItem = new ImageItem("Measuring", this.animationImageA, Item.LAYOUT_CENTER, "A");
		f.append(this.animationItem);
		this.runAnimation();
		this.measurementStatusString = new StringItem( "Counter:", "");
		f.append(this.measurementStatusString);
		f.addCommand(exitCmd);
		f.setCommandListener(this);
		return f;
	}
	
	private void showConfigurationDisplay(){
		Form f = buildConfigurationDisplay();
		display.setCurrent(f);
	}
	
	private void showMeasurementDisplay(){
		Form f = buildMeasurementDisplay();
		display.setCurrent(f);
	}

	private void showErrorDisplay(Exception exception){
		Form f = new Form("ERROR");
		f.append("Detalles:\n");
		f.append(exception.getMessage()+ "\n");
		f.append(exception.getClass().getName()+ "\n");
		f.addCommand(welcomeCmd);
		f.addCommand(exitCmd);
		f.setCommandListener(this);
		display.setCurrent(f);
	}
	
	private void showWelcomeDisplay(){
		Form f = new Form("Welcome");
		f.append("SENSORNET AMOTE\nVersion Beta 1.0 Build 20101020\n");
		f.append("\nNode ID:" + Configuration.SENSOR_ID);
		f.append("Server:" + Configuration.SERVER_URL);
		f.append("Interval:" + Configuration.RECORDING_INTERVAL);
		f.append("Duration:" + Configuration.RECORDING_DURATION);
		f.addCommand(measureCmd);
		f.addCommand(configCmd);
		f.addCommand(exitCmd);
		f.setCommandListener(this);
		display.setCurrent(f);
	}
	
	private void doUpdateConfiguration(Displayable d) throws Exception{
		// save sensor_id value 
		Form f = (Form)d;
		TextField txt = (TextField)f.get(0);
		String id = txt.getString();
		Configuration.SENSOR_ID = id;
		txt = (TextField)f.get(1);
		Configuration.SERVER_URL = txt.getString();
		txt = (TextField)f.get(2);
		Configuration.RECORDING_DURATION = Long.parseLong(txt.getString());
		txt = (TextField)f.get(3);
		Configuration.RECORDING_INTERVAL = Integer.parseInt(txt.getString());
		
		preferences.put(CONFIG_SENSOR_ID, Configuration.SENSOR_ID);
		preferences.put(CONFIG_KEY_SERVER_URL, Configuration.SERVER_URL);
		preferences.put(CONFIG_KEY_RECORDING_DURATION, Long.toString(Configuration.RECORDING_DURATION));
		preferences.put(CONFIG_KEY_RECORDING_INTERVAL, Integer.toString(Configuration.RECORDING_INTERVAL));
		
		preferences.save();
		//d.setTitle("Saved " + id);
		//System.err.println("title:"+d.getTitle() + ":");
		//System.err.println("item:label=" + txt.getLabel());
		//System.err.println("item:value=" + txt.getString());
	}
	
	private void loadConfiguration() throws Exception{
		preferences = new Preferences(CONFIG_FILE);
		String sensorId = preferences.get(CONFIG_SENSOR_ID);
		if (sensorId == null) {
			sensorId = "DEFAULT";
		}
		else {
			Configuration.RECORDING_INTERVAL = Integer.parseInt(preferences.get(CONFIG_KEY_RECORDING_INTERVAL));
			Configuration.RECORDING_DURATION= Integer.parseInt(preferences.get(CONFIG_KEY_RECORDING_DURATION));
			Configuration.SERVER_URL = preferences.get(CONFIG_KEY_SERVER_URL);
		}
		Configuration.SENSOR_ID = sensorId;
	}
	
	private void startMeasurement(){
		sensornet = new SensornetApp();
		sensornet.setListener(this);
		sensornet.start();
	}

	public void sensorAction(int type, String info) {
		String status = info + "\n" + this.sensornet.getDebugInfo();
		this.measurementStatusString.setText(status);
	}

	private void runAnimation(){
		Thread t = new Thread(new Runnable() {
			public void run() {
				try {
					while(true){
						// switch animation image
						String state = animationItem.getAltText();
						String label = "B";
						Image img = animationImageB;
						if (state == "B") {
							label = "A";
							img = animationImageA;
						}
						animationItem.setImage(img);
						animationItem.setAltText(label);
						// delay 1 second
						Thread.sleep(1*1000);
					}
				} catch (Exception e) {
					showErrorDisplay(e);
				};
			}
		});
		t.start();
	}
	
}
